/**
 * Copyright (C) 2008 Huascar A. Sanchez.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.gotobject.inject.resources;

import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.gotobject.inject.util.Utils;
import com.gotobject.inject.binder.ResourceBindingBuilder;
import com.gotobject.inject.binder.LoadedResourceBindingBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Map;
import java.io.IOException;

/**
 * <p>
 * the base class for all {@link ResourceModule modules} objects where the resource loading
 * magic occurs.
 *
 * </p>
 * This class is an extension of the traditional {@link AbstractModule Guice's AbstractModule}.
 * This module contains a new {@link #bindResource() method}.
 *
 *
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public abstract class ResourceModule extends AbstractModule {
    /**
     * @return a {@link com.gotobject.inject.binder.LoadedResourceBindingBuilder} object.
     */
    protected LoadedResourceBindingBuilder bindResource(){
        return new ResourceBinderAdapter(binder()).bindResource();
    }

    /**
     * private implementation of {@link com.gotobject.inject.resources.ResourceBinder} class.
     */
    private static class ResourceBinderAdapter implements ResourceBinder {
        private final Binder binder;
        ResourceBinderAdapter(Binder binder){
            this.binder = binder;
        }

        public LoadedResourceBindingBuilder bindResource() {
            return new LoadedResourceBindingBuilderImpl(getBinder());
        }

        private Binder getBinder(){
            return binder;
        }
    }

    /**
     * private implementation of {@link com.gotobject.inject.binder.LoadedResourceBindingBuilder}
     * class.
     */
    static class LoadedResourceBindingBuilderImpl implements LoadedResourceBindingBuilder {
        private final List<Object[]> config;
        private final Binder         binder;
        private       boolean        isRead;
        LoadedResourceBindingBuilderImpl(Binder binder){
            this.binder = binder;
            config = new ArrayList<Object[]>();
        }

        public ResourceBindingBuilder annotatedWith(Resource annotation, String value) {
            Resources.from(annotation, value, binder);
            return this;
        }

        public ResourceBindingBuilder fromDefinition(Map<String, String> map) {
            Resources.from(map, binder);
            return this;
        }

        public LoadedResourceBindingBuilder fromDefinition(Object[] path) {
            config.add(path);
            return this;
        }

        private List<Object[]> getConfig(){
            return Collections.unmodifiableList(config);
        }


        private boolean isRead(){
            return isRead;
        }

        private void read(){
            isRead = true;
        }

        private void readConfig() throws IOException {
            for(Object[] eachArray : getConfig()){
                final Object first  = eachArray[0];
                final Object second = eachArray[1];

                // .. always: first arg is the enclosing class
                // .. always: second arg is the name of the inject file
                final Class<?> enclosingClass = Class.class.cast(first);
                final String   path           = String.class.cast(second);

                final Properties props = Utils.loadProperties(enclosingClass, path);
                Resources.from(props, binder);
                read();
            }
        }

        public void to(Class<?> cls) {
            try {
                if(!isRead()){
                    readConfig();
                }
                binder.bind(cls).asEagerSingleton();
            } catch(Exception e){
                binder.addError(e);
            }
        }

        public void toMultiple(Class<?>... classes) {
            for(Class<?> eachClass : classes){
                to(eachClass);
            }
        }

    }

}
